package com.glodon.paas.search.query;

import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.SearchHits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.glodon.paas.search.model.HitFiles;
import com.glodon.paas.search.model.QueryBase;
import com.glodon.paas.search.util.ElasticSearchUtils;

public abstract class AbstractQueryService<I, Q extends QueryBase> implements QueryService<I, Q> {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    protected ElasticSearchUtils esUtils;
    // 查询时需要返回的field
    protected String[] projectiveField;

    protected BeanUtilsBean utilsBean;

    @PostConstruct
    public void init() {
        utilsBean = new BeanUtilsBean();
        DateConverter dateConverter = new DateConverter();
        dateConverter.setPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        utilsBean.getConvertUtils().register(dateConverter, Date.class);
        projectiveField = StringUtils.split(getFields(), ",");
    }

    public HitFiles<I> query(String index, String type, Q file) {
        Client client = esUtils.getClient();
        try {
            SearchRequestBuilder builder = client.prepareSearch(index);
            QueryBuilder queryBuilder = prepareQueryBuilder(file);
            if (queryBuilder != null) {
                builder.setQuery(queryBuilder);
            }
            FilterBuilder filterBuilder = prepareFilterBuilder(file);
            if (filterBuilder != null) {
                builder.setFilter(filterBuilder);
            }
            builder.addFields(projectiveField);
            builder.setFrom(file.getFrom());
            builder.setSize(file.getSize());
            SearchResponse response = builder.execute().actionGet();
            SearchHits hits = response.getHits();
            HitFiles<I> hitDocs = new HitFiles<I>();
            hitDocs.setTookInMillis(response.getTookInMillis());
            hitDocs.setTotalHits(hits.getTotalHits());
            logger.debug("search: {}, hit:{} elapsed time:{}", builder.toString(), hitDocs.getTotalHits(),
                    hitDocs.getTookInMillis());
            int minSize = Math.min((int) hits.getTotalHits(), file.getSize());
            for (int i = 0; i < minSize; i++) {
                SearchHit hit = hits.getAt(i);
                Map<String, SearchHitField> map = hit.getFields();
                I iDoc = createIndexObject();
                for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) {
                    String field = iterator.next();
                    SearchHitField hitField = map.get(field);
                    Object value = hitField.getValue();
                    logger.debug(field + " - " + value);
                    try {
                        utilsBean.setProperty(iDoc, field, value);
                    } catch (Exception e) {
                        logger.error("failed to convert from search field to pojo's properties", e);
                    }
                }
                hitDocs.addHit(iDoc);
            }
            return hitDocs;
        } finally {
            esUtils.closeClient(client);
        }
    }

    public abstract String getFields();

    public abstract QueryBuilder prepareQueryBuilder(Q file);

    public abstract FilterBuilder prepareFilterBuilder(Q file);

    public abstract I createIndexObject();
}